/*
 *  Copyright (C) 2010 Zubanov Dmitry
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jssdb.core;

import java.io.File;
import java.io.FileReader;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jssdb.core.filesystem.ModifyLogger;
import org.jssdb.core.handler.FileStorageHandler;
import org.jssdb.core.net.DataChunkSize;
import org.jssdb.core.net.MulticastServer;
import org.jssdb.core.net.ReplicationServer;
import org.jssdb.core.net.Server;
import org.jssdb.core.ram.RamHandler;
import org.jssdb.core.utils.InitException;

/**
 *
 * @author developer
 */
public class DBProperties {

    private static final Logger logger = Logger.getLogger(DBProperties.class.getName());
    private String externalJarFiles[];
    private String pathToDB, bindHost = "localhost", replicationHost = "localhost",
            multicastAddress = "230.0.0.1";
    private int bindPort = 5500, autoStart = 0, autoIncrement = 0, autoOffset = 1,
            multicastPort = 6000, replicationPort = 7000,
            transferBlockSize = 49152,
            changeWatcherTimeout = 10000, memoryModel = 1;
    private boolean networkEnable = false, replicationEnable = false,
            changeWatcherEnable = false;
    public static int RAM = 1, STORAGE = 2;
    public boolean isInit = false;

    private DBProperties() {
    }

    public void forceInitMe() {
        isInit = false;
        initMe();
    }

    public void initMe() {
        if(isInit){
            return;
        }
        FileStorageHandler.getDefault().setIdOffset(getAutoStart(),
                getAutoIncrement(), getAutoOffset());

        for (String jar : getExternalJarFiles()) {
            try {
                ExternalClassLoader.getInstance().addJarFile(jar);
            } catch (Exception ex) {
                Logger.getLogger(JSSDB.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        if (isReplicationEnable()) {
            ReplicationServer replicationServer = ReplicationServer.getInstance();
            replicationServer.setEnable(isReplicationEnable());
            replicationServer.setHost(getBindHost());
            replicationServer.setPort(getReplicationPort());
            replicationServer.start();

            MulticastServer multicastServer = MulticastServer.getInstance();
            multicastServer.setMulticastAddress(getMulticastAddress());
            multicastServer.setPort(getMulticastPort());
            multicastServer.start();
        }

        if (isChangeWatcherEnable()) {
            RamHandler.getInstance().setWatcherStatus(isChangeWatcherEnable());
        }

        if (getMemoryModel() == DBProperties.RAM) {
            RamHandler.getInstance().initDB();
        } else {
            // TODO
        }

        if (isNetworkEnable()) {
            Server server = Server.getInstance();
            server.setBindHost(getBindHost());
            server.setPort(getBindPort());
            server.start();
        }

        DataChunkSize.getInstance().setSize(getTransferBlockSize());
    }

    public void setProperties(String file) {
        Properties p = new Properties();
        try {
            p.load(new FileReader(new File(file)));
            String val = p.getProperty("external_jar_files");
            if (val == null) {
                throw new InitException("Path to external jar files can't be is null");
            } else {
                String arr[] = val.split(",");
                externalJarFiles = new String[arr.length];
                int i = 0;
                for (String s : arr) {
                    externalJarFiles[i] = s;
                    i++;
                }
            }

            val = p.getProperty("path_to_db");
            if (val == null) {
                throw new InitException("Path to db folder can't be is null");
            } else {
                pathToDB = val;
            }

            val = p.getProperty("host");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Bind host can't be is empty string");
                } else {
                    bindHost = val;
                    replicationHost = bindHost;
                }
            }

            val = p.getProperty("port");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Bind port can't be is empty string");
                } else {
                    try {
                        bindPort = Integer.parseInt(val);
                    } catch (Exception e) {
                        logger.log(Level.SEVERE, val, new InitException("Port number invalid"));
                    }

                }
            }

            val = p.getProperty("replication_port");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Bind port can't be is empty string");
                } else {
                    try {
                        replicationPort = Integer.parseInt(val);
                    } catch (Exception e) {
                        logger.log(Level.SEVERE, val, new InitException("Port number invalid"));
                    }

                }
            }

            val = p.getProperty("multicast_address");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Multicast address can't be is empty");
                } else {
                    multicastAddress = val;
                }
            }

            val = p.getProperty("memory_model");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Memory model can't be is empty");
                } else {
                    if (val.trim().equals("ram")) {
                        memoryModel = RAM;
                    } else {
                        memoryModel = STORAGE;
                    }
                }
            }

            val = p.getProperty("multicast_port");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Multicast port can't be is empty");
                } else {
                    try {
                        multicastPort = Integer.parseInt(val);
                    } catch (Exception e) {
                        logger.log(Level.SEVERE, val, new InitException("Port number invalid"));
                    }
                }
            }

            val = p.getProperty("auto_start");
            if (val == null) {
                throw new InitException("Auto start value can't be is null");
            } else {
                try {
                    autoStart = Integer.parseInt(val);
                } catch (Exception e) {
                    logger.log(Level.SEVERE, val, new InitException("Auto start value invalid"));
                }
            }

            val = p.getProperty("auto_increment");
            if (val == null) {
                throw new InitException("Auto increment value can't be is null");
            } else {
                try {
                    autoIncrement = Integer.parseInt(val);
                } catch (Exception e) {
                    logger.log(Level.SEVERE, val, new InitException("Auto increment value invalid"));
                }
            }

            val = p.getProperty("auto_offset");
            if (val == null) {
                throw new InitException("Auto offset value can't be is null");
            } else {
                try {
                    autoOffset = Integer.parseInt(val);
                } catch (Exception e) {
                    logger.log(Level.SEVERE, val, new InitException("Auto offset value invalid"));
                }
            }

            val = p.getProperty("transfer_block_size");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Transfer block size value can't be is empty");
                } else {
                    try {
                        transferBlockSize = Integer.parseInt(val);
                    } catch (Exception e) {
                        logger.log(Level.SEVERE, val, new InitException("Transfer block size value invalid"));
                    }
                }
            }

            val = p.getProperty("change_watcher_timeout");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Change watcher timeout value can't be is empty");
                } else {
                    try {
                        changeWatcherTimeout = Integer.parseInt(val);
                    } catch (Exception e) {
                        logger.log(Level.SEVERE, val, new InitException("Change watcher timeout value invalid"));
                    }
                }
            }

            val = p.getProperty("is_replication_enable");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Is replication enable value can't be is empty");
                } else {
                    if (val.equals("true")) {
                        replicationEnable = true;
                    } else {
                        replicationEnable = false;
                    }
                }
            }

            val = p.getProperty("is_change_watcher_enable");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Is change watcher enable value can't be is empty");
                } else {
                    if (val.equals("true")) {
                        changeWatcherEnable = true;
                    } else {
                        changeWatcherEnable = false;
                    }
                }
            }

            val = p.getProperty("is_network_enable");
            if (val != null) {
                if (val.equals("")) {
                    throw new InitException("Is network enable value can't be is null");
                } else {
                    if (val.equals("true")) {
                        networkEnable = true;
                    } else {
                        networkEnable = false;
                    }
                }
            }

        } catch (Exception ex) {
            logger.log(Level.SEVERE, file, ex);
        }
    }

    public void setMemoryModel(int model) {
        memoryModel = model;
    }

    public int getMemoryModel() {
        return memoryModel;
    }

    public int getAutoIncrement() {
        return autoIncrement;
    }

    public void setAutoIncrement(int autoIncrement) {
        this.autoIncrement = autoIncrement;
    }

    public int getAutoOffset() {
        return autoOffset;
    }

    public void setAutoOffset(int autoOffset) {
        this.autoOffset = autoOffset;
    }

    public int getAutoStart() {
        return autoStart;
    }

    public void setAutoStart(int autoStart) {
        this.autoStart = autoStart;
    }

    public String getBindHost() {
        return bindHost;
    }

    public void setBindHost(String bindHost) {
        this.bindHost = bindHost;
    }

    public int getBindPort() {
        return bindPort;
    }

    public void setBindPort(int bindPort) {
        this.bindPort = bindPort;
    }

    public boolean isChangeWatcherEnable() {
        return changeWatcherEnable;
    }

    public void setChangeWatcherEnable(boolean changeWatcherEnable) {
        this.changeWatcherEnable = changeWatcherEnable;
    }

    public int getChangeWatcherTimeout() {
        return changeWatcherTimeout;
    }

    public void setChangeWatcherTimeout(int changeWatcherTimeout) {
        this.changeWatcherTimeout = changeWatcherTimeout;
    }

    public String[] getExternalJarFiles() {
        return externalJarFiles;
    }

    public void setExternalJarFiles(String externalJarFiles[]) {
        this.externalJarFiles = externalJarFiles;
    }

    public String getMulticastAddress() {
        return multicastAddress;
    }

    public void setMulticastAddress(String multicastAddress) {
        this.multicastAddress = multicastAddress;
    }

    public int getMulticastPort() {
        return multicastPort;
    }

    public void setMulticastPort(int multicastPort) {
        this.multicastPort = multicastPort;
    }

    public boolean isNetworkEnable() {
        return networkEnable;
    }

    public void setNetworkEnable(boolean networkEnable) {
        this.networkEnable = networkEnable;
    }

    public String getPathToDB() {
        return pathToDB;
    }

    public void setPathToDB(String pathToDB) {
        this.pathToDB = pathToDB;
    }

    public boolean isReplicationEnable() {
        return replicationEnable;
    }

    public void setReplicationEnable(boolean replicationEnable) {
        this.replicationEnable = replicationEnable;
    }

    public int getReplicationPort() {
        return replicationPort;
    }

    public void setReplicationPort(int replicationPort) {
        this.replicationPort = replicationPort;
    }

    public int getTransferBlockSize() {
        return transferBlockSize;
    }

    public void setTransferBlockSize(int transferBlockSize) {
        this.transferBlockSize = transferBlockSize;
    }

    public static DBProperties getInstance() {
        return DBPropertiesHolder.INSTANCE;
    }

    private static class DBPropertiesHolder {

        private static final DBProperties INSTANCE = new DBProperties();
    }
}
